package org.dicr.gui;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;

import org.dicr.event.*;
import org.dicr.gui.io.*;
import org.dicr.gui.res.*;

/***********************************************************************************************************************
 * @author Igor A Tarasov me@dicr.dn.ua
 * @version 021025
 **********************************************************************************************************************/
public class ImageViewerPanel extends JPanel implements ComponentListener, ActionListener, Editable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	// компоненты панели изображения
	protected JScrollPane scroll = new JScrollPane();

	protected JLabel imageLabel = new JLabel();

	protected JLabel zoomLabel = new JLabel();

	// панель управления
	protected ControlPanel controlPanel = new ControlPanel();

	protected JButton plusButton = new JButton();

	protected JButton actualButton = new JButton();

	protected JButton minusButton = new JButton();

	protected JButton matchButton = new JButton();

	protected JButton loadButton = new JButton();

	protected JButton saveButton = new JButton();

	protected JButton clearButton = new JButton();

	protected JButton[] buttons = new JButton[] { this.loadButton, this.saveButton, this.clearButton, this.plusButton,
	        this.actualButton, this.minusButton, this.matchButton };

	// изображение
	protected ImageIcon icon = null;

	protected int zoom = ZOOM_ACTUAL;

	public static final int ZOOM_AUTO = 0;

	public static final int ZOOM_ACTUAL = 1;

	public static final int ZOOM_MIN = -10;

	public static final int ZOOM_MAX = 4;

	// события
	protected Vector<ChangeListener> listeners = new Vector<ChangeListener>();

	// файлы
	protected ImageIO imageIO = new ImageIO();

	protected boolean editable = true;

	/*******************************************************************************************************************
	 * Конструктор
	 ******************************************************************************************************************/
	public ImageViewerPanel() {
		try {
			jbInit();
		} catch (Exception ex) {
			DicrExceptionHandler.handleException(ex, this);
		}
	}

	/*******************************************************************************************************************
	 * Инициализация
	 ******************************************************************************************************************/
	private void jbInit() {
		ResourceManager.setButtonReource(this.plusButton, "icons/ZoomIn24.gif", "+", "Увеличить");
		ResourceManager.setButtonReource(this.actualButton, "icons/Zoom24.gif", "1:1", "Оригинальный размер");
		ResourceManager.setButtonReource(this.minusButton, "icons/ZoomOut24.gif", "-", "Уменьшить");
		ResourceManager.setButtonReource(this.matchButton, "icons/Stop24_2.gif", "A", "Масштабировать автоматически");
		ResourceManager.setButtonReource(this.clearButton, "icons/Delete24.gif", "D", "Удалить изображение");
		ResourceManager.setButtonReource(this.loadButton, "icons/Open24.gif", "L", "Загрузить изображение из файла");
		ResourceManager.setButtonReource(this.saveButton, "icons/Save24.gif", "S", "Сохранить изображение в файл");
		Insets margin = new Insets(0, 0, 0, 0);
		for (int i = 0; i < this.buttons.length; i++) {
			JButton button = this.buttons[i];
			button.addActionListener(this);
			button.setMargin(margin);
		}
		this.controlPanel.add(this.loadButton);
		this.controlPanel.add(this.saveButton);
		this.controlPanel.add(this.clearButton);
		this.controlPanel.addSeparator();
		this.controlPanel.add(this.plusButton);
		this.controlPanel.add(this.actualButton);
		this.controlPanel.add(this.minusButton);
		this.controlPanel.add(this.matchButton);
		this.controlPanel.addSeparator();
		this.zoomLabel.setHorizontalAlignment(SwingConstants.CENTER);
		this.imageLabel.setHorizontalAlignment(SwingConstants.CENTER);
		this.imageLabel.setVerticalAlignment(SwingConstants.CENTER);
		this.imageLabel.setAutoscrolls(true);
		this.scroll.setViewportView(this.imageLabel);
		this.scroll.setPreferredSize(new Dimension(250, 250));
		this.scroll.addComponentListener(this);
		this.setLayout(new BorderLayout());
		this.add(this.scroll, BorderLayout.CENTER);
		this.add(this.controlPanel, BorderLayout.NORTH);
		this.add(this.zoomLabel, BorderLayout.SOUTH);
	}

	/*******************************************************************************************************************
	 * Установка признака редактируемости
	 ******************************************************************************************************************/
	@Override
    public void setEditable(boolean theEditable) {
		if (this.editable != theEditable) {
			this.editable = theEditable;
			updateButtonState();
		}
	}

	/*******************************************************************************************************************
	 * Возвращает признак редактируемости
	 ******************************************************************************************************************/
	@Override
    public boolean isEditable() {
		return this.editable;
	}

	/*******************************************************************************************************************
	 * Обновляет состояние кнопок
	 ******************************************************************************************************************/
	protected void updateButtonState() {
		boolean haveicon = this.icon != null;
		boolean autozoom = (this.zoom == ZOOM_AUTO);
		this.plusButton.setEnabled(haveicon && (this.zoom < ZOOM_MAX) || autozoom);
		this.minusButton.setEnabled(haveicon && (this.zoom > ZOOM_MIN) || autozoom);
		this.actualButton.setEnabled(haveicon && (this.zoom != ZOOM_ACTUAL));
		this.matchButton.setEnabled(haveicon && !autozoom);
		this.saveButton.setEnabled(haveicon);
		this.loadButton.setEnabled(this.editable);
		this.clearButton.setEnabled(haveicon && this.editable);
	}

	/*******************************************************************************************************************
	 * Возвращает масштабированное изображение из окна просмотра
	 ******************************************************************************************************************/
	public Image getViewedImage() {
		return (this.imageLabel.getIcon() != null) ? ((ImageIcon) this.imageLabel.getIcon()).getImage() : null;
	}

	/*******************************************************************************************************************
	 * Возвращает актуальное изображение в масштабе 1:1
	 ******************************************************************************************************************/
	public Image getImage() {
		return (this.icon != null) ? this.icon.getImage() : null;
	}

	/*******************************************************************************************************************
	 * Изменяет размер изображения
	 ******************************************************************************************************************/
	protected void resizeImage() {
		this.setCursor(GuiMisc.CURSOR_WAIT);
		try {
			if (this.icon != null) {
				if (this.zoom == ZOOM_ACTUAL) this.imageLabel.setIcon(this.icon);
				else {
					Image image = this.icon.getImage();
					Dimension imageSize = new Dimension(this.icon.getIconWidth(), this.icon.getIconHeight());
					float iz = 1.0f;
					if (this.zoom == ZOOM_AUTO) {
						Dimension viewSize = this.scroll.getViewport().getExtentSize();
						iz = Math.min((float) viewSize.width / (float) imageSize.width, (float) viewSize.height
						        / (float) imageSize.height);
					} else iz = (this.zoom < 0) ? -1.0f / this.zoom : (float) this.zoom;
					Dimension newSize = new Dimension((int) (imageSize.width * iz), (int) (imageSize.height * iz));
					if (newSize.width < 1) newSize.width = 1;
					if (newSize.height < 1) newSize.height = 1;
					this.imageLabel.setIcon(new ImageIcon(image.getScaledInstance(newSize.width, newSize.height,
					        Image.SCALE_FAST)));
				}
			} else this.imageLabel.setIcon(null);
			if (this.zoom == ZOOM_AUTO) this.zoomLabel.setText("Масштаб: автоматически");
			else {
				int ma = (this.zoom < 0) ? -this.zoom : 1;
				int mb = (this.zoom > 0) ? this.zoom : 1;
				this.zoomLabel.setText("Масштаб: " + ma + ":" + mb);
			}
			updateButtonState();
		} catch (Exception ex) {
			DicrExceptionHandler.handleException(ex, this);
		}
		this.setCursor(GuiMisc.CURSOR_DEFAULT);
	}

	/*******************************************************************************************************************
	 * Изменение размера изображения
	 ******************************************************************************************************************/
	public boolean setZoom(int theZoom) {
		boolean ret = false;
		if (theZoom >= ZOOM_MIN && theZoom <= ZOOM_MAX) {
			this.zoom = theZoom;
			resizeImage();
			fireChangeEvent();
			ret = true;
		}
		return ret;
	}

	/*******************************************************************************************************************
	 * Загружает изображение из иконки
	 ******************************************************************************************************************/
	public void setImage(ImageIcon imageIcon) {
		this.icon = imageIcon;
		this.zoom = 0;
		setZoom(ZOOM_ACTUAL);
	}

	/*******************************************************************************************************************
	 * загружает рисунок из потока ввода/вывода
	 ******************************************************************************************************************/
	public void loadImage(InputStream in) throws IOException {
		ImageIcon image = this.imageIO.loadImage(in);
		setImage(image);
	}

	/*******************************************************************************************************************
	 * Выводит диалог загрузки изображения из файла
	 ******************************************************************************************************************/
	public void loadImage() throws IOException, FileNotFoundException {
		ImageIcon image = this.imageIO.loadImage(this);
		if (image != null) setImage(image);
	}

	/*******************************************************************************************************************
	 * Записывает изображение в поток ввода-вывода
	 ******************************************************************************************************************/
	public void saveImage(OutputStream ou) {
		if (this.icon != null) this.imageIO.saveImage(ou, this.icon.getImage());
	}

	/*******************************************************************************************************************
	 * Выводит диалог сохранения изображения в файле
	 ******************************************************************************************************************/
	public void saveImage() throws IOException, FileNotFoundException {
		if (this.icon != null) {
			Image image = null;
			if (this.zoom != ZOOM_ACTUAL
			        && JOptionPane.showConfirmDialog(this,
			                "Масштаб изображения изменен. Установить масштаб 1:1 перед сохранением ?",
			                "Сохранение изображения в файл", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.NO_OPTION) image = getViewedImage();
			else image = this.icon.getImage();
			this.imageIO.saveImage(image, this);
		}
	}

	/*******************************************************************************************************************
	 * Обработчик событий панели
	 ******************************************************************************************************************/
	@Override
    public void componentShown(ComponentEvent e) {
	//
	}

	@Override
    public void componentMoved(ComponentEvent e) {
	//
	}

	@Override
    public void componentHidden(ComponentEvent e) {
	//
	}

	@Override
    public void componentResized(ComponentEvent e) {
		try {
			if (this.zoom == ZOOM_AUTO) resizeImage();
		} catch (Exception ex) {
			DicrExceptionHandler.handleException(ex, this);
		}
	}

	/*******************************************************************************************************************
	 * Обработчик событий кнопок
	 ******************************************************************************************************************/
	@Override
    public void actionPerformed(ActionEvent e) {
		try {
			Object src = e.getSource();
			if (src == this.plusButton) setZoom((this.zoom == -1) ? 2 : this.zoom + 1);
			else if (src == this.minusButton) setZoom((this.zoom == 1) ? -2 : this.zoom - 1);
			else if (src == this.actualButton) setZoom(ZOOM_ACTUAL);
			else if (src == this.matchButton) setZoom(ZOOM_AUTO);
			else if (src == this.clearButton) setImage((ImageIcon) null);
			else if (src == this.loadButton) loadImage();
			else if (src == this.saveButton) saveImage();
		} catch (Exception ex) {
			DicrExceptionHandler.handleException(ex, this, true);
		}
	}

	/*******************************************************************************************************************
	 * Добавляет слушателя
	 ******************************************************************************************************************/
	public void addChangeListener(ChangeListener l) {
		if (l != null && !this.listeners.contains(l)) this.listeners.add(l);
	}

	/*******************************************************************************************************************
	 * Удаляет слушателя
	 ******************************************************************************************************************/
	public void removeChangeListener(ChangeListener l) {
		this.listeners.remove(l);
	}

	/*******************************************************************************************************************
	 * Оповещает слушателей
	 ******************************************************************************************************************/
	protected void fireChangeEvent() {
		for (int i = 0; i < this.listeners.size(); i++)
			this.listeners.get(i).stateChanged(new ChangeEvent(this));
	}
}
